﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using Orchard.Caching;
using Orchard.Data;
using Orchard.Environment.Extensions;
using So.ShapeViewCounter.Models;

namespace So.ShapeViewCounter.Services
{
    [OrchardFeature("So.ShapeUserViewCounter")]
    public class ShapeUserViewCounterService : IShapeUserViewCounterService
    {
        private readonly ICacheManager _cacheManager;
        private readonly ISignals _signals;
        private readonly IRepository<ShapeUserViewCounterPartRecord> _shapeUserViewCounterRecordRepository;

        public ShapeUserViewCounterService(IRepository<ShapeUserViewCounterPartRecord> shapeUserViewCounterRecordRepository, ICacheManager cacheManager, ISignals signals) {
            _shapeUserViewCounterRecordRepository = shapeUserViewCounterRecordRepository;
            _cacheManager = cacheManager;
            _signals = signals;
        }

        /// <summary>
        /// Update or insert new view info for given shape and userid
        /// </summary>
        /// <param name="shapeName"></param>
        /// <param name="contentItemId"> </param>
        /// <param name="userId"></param>
        public void UpSertShapeUserView(string shapeName, int contentItemId, int userId)
        {
            _signals.Trigger(CacheInvalidateTriggers.ShapeUserViewCounterChangedForShapeName(shapeName));
            _signals.Trigger(CacheInvalidateTriggers.ShapeUserViewCounterChangedForContentItemId(contentItemId));
            _signals.Trigger(CacheInvalidateTriggers.ShapeUserViewCounterChangedForUserId(userId));

            var record = GetLastUserShapeView(shapeName, contentItemId, userId);
            if (record != null)
            {
                record.ViewCount++;
                record.LastViewed = DateTime.Now;
                _shapeUserViewCounterRecordRepository.Update(record);
            }
            else
            {
                record = new ShapeUserViewCounterPartRecord
                             {
                    ContentItemId = contentItemId,
                    ViewCount = 1,
                    ShapeName = shapeName,
                    UserId = userId,
                    LastViewed = DateTime.Now
                };
                _shapeUserViewCounterRecordRepository.Create(record);
            }
        }
        
        public IEnumerable<int> GetViewedContentIds(string shapeName, int userId)
        {
            string cacheKey = Constants._cachePrefixKey + "GetViewedContentIds_" + shapeName + "_" + userId;
            var cachedRetval = _cacheManager.Get(cacheKey, ctx =>
            {
                var retVal = _shapeUserViewCounterRecordRepository.Fetch(s => s.ShapeName == shapeName && s.UserId == userId).Select(s => s.ContentItemId);
                ctx.Monitor(_signals.When(CacheInvalidateTriggers.ShapeUserViewCounterChangedForShapeName(shapeName)));
                ctx.Monitor(_signals.When(CacheInvalidateTriggers.ShapeUserViewCounterChangedForUserId(userId)));
                return retVal;
            });
            return cachedRetval;
        }

        public ShapeUserViewCounterPartRecord GetLastUserShapeView(string shapeName, int contentItemId, int userId)
        {
            string cacheKey = Constants._cachePrefixKey + "GetLastUserShapeView_" + shapeName + "_" + contentItemId.ToString(CultureInfo.InvariantCulture) + "_" + userId;
            var cachedRetval = _cacheManager.Get(cacheKey, ctx =>
            {
                var retVal = _shapeUserViewCounterRecordRepository.Get(s => s.ShapeName == shapeName && s.ContentItemId == contentItemId && s.UserId == userId);
                ctx.Monitor(_signals.When(CacheInvalidateTriggers.ShapeUserViewCounterChangedForShapeName(shapeName)));
                ctx.Monitor(_signals.When(CacheInvalidateTriggers.ShapeUserViewCounterChangedForContentItemId(contentItemId)));
                ctx.Monitor(_signals.When(CacheInvalidateTriggers.ShapeUserViewCounterChangedForUserId(userId)));
                return retVal;
            });
            return cachedRetval;
        }

        public int GetTotalContentItemShapeViewCount(int contentItemId)
        {
            string cacheKey = Constants._cachePrefixKey + "GetTotalContentItemShapeViewCount_" + contentItemId.ToString(CultureInfo.InvariantCulture);
            var cachedRetval = _cacheManager.Get(cacheKey, ctx =>
            {
                var retVal = _shapeUserViewCounterRecordRepository.Fetch(s => s.ContentItemId == contentItemId).Sum(s => s.ViewCount);
                ctx.Monitor(_signals.When(CacheInvalidateTriggers.ShapeUserViewCounterChangedForContentItemId(contentItemId)));
                return retVal;
            });
            return cachedRetval;
        }

        public int GetTotalContentItemShapeViewCount(string shapeName, int contentItemId)
        {
            string cacheKey = Constants._cachePrefixKey + "GetTotalContentItemShapeViewCount_" + shapeName + "_"+ contentItemId.ToString(CultureInfo.InvariantCulture);
            var cachedRetval = _cacheManager.Get(cacheKey, ctx =>
            {
                var retVal = _shapeUserViewCounterRecordRepository.Fetch(s => s.ShapeName == shapeName && s.ContentItemId == contentItemId).Sum(s=> s.ViewCount);
                ctx.Monitor(_signals.When(CacheInvalidateTriggers.ShapeUserViewCounterChangedForShapeName(shapeName)));
                ctx.Monitor(_signals.When(CacheInvalidateTriggers.ShapeUserViewCounterChangedForContentItemId(contentItemId)));
                return retVal;
            });
            return cachedRetval;
        }
    }
}